package edu.northwestern.cbits.purple_robot_manager.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.StatFs;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.net.ssl.SSLException;
import edu.northwestern.cbits.purple_robot_manager.EncryptionManager;
import edu.northwestern.cbits.purple_robot_manager.logging.LogManager;
/**
* Created by Chris Karr on 6/2/2015.
*/
public class ImageUtils {
private static long _lastSpaceChecked = -1;
private static long _lastCacheAccess = -1;
private static long _lastSpaceFree = -1;
private static File _cacheDir = null;
public static Uri fetchResizedImageSync(final Context context, final Uri uri, final int width, final int height, final boolean fillSpace)
{
if (uri == null)
return null;
Uri resized = Uri.parse(uri.toString() + "/" + height + "-" + width);
String hashString = EncryptionManager.getInstance().createHash(context, resized.toString());
final File cachedFile = new File(ImageUtils.fetchCacheDir(context), hashString);
if (cachedFile.exists())
{
cachedFile.setLastModified(System.currentTimeMillis());
return Uri.fromFile(cachedFile);
}
else
{
Uri cached = ImageUtils.fetchCachedUri(context, uri, false, null);
if (cached != null)
{
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
opts.inInputShareable = true;
opts.inPurgeable = true;
BitmapFactory.decodeFile(cached.getPath(), opts);
int scale = 1;
int thisWidth = width;
int thisHeight = height;
while ((thisWidth > 0 && thisHeight > 0) && (opts.outWidth > thisWidth || opts.outHeight > thisHeight))
{
scale += 1;
thisWidth = thisWidth * 2;
thisHeight = thisHeight * 2;
}
if (scale < 1)
scale = 1;
if (fillSpace)
{
// CJK: Memory fix?
scale += 1;
}
opts = new BitmapFactory.Options();
opts.inDither = false;
opts.inPurgeable = true;
opts.inInputShareable = true;
opts.inSampleSize = scale;
try
{
Bitmap bitmap = BitmapFactory.decodeFile(cached.getPath(), opts);
if (bitmap != null)
{
cachedFile.createNewFile();
FileOutputStream fout = new FileOutputStream(cachedFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fout);
fout.close();
bitmap.recycle();
return Uri.fromFile(cachedFile);
}
}
catch (OutOfMemoryError e)
{
System.gc();
}
catch (FileNotFoundException e)
{
LogManager.getInstance(context).logException(e);
}
catch (IOException e)
{
LogManager.getInstance(context).logException(e);
}
}
}
return null;
}
public static Uri fetchCachedUri(final Context context, final Uri uri, boolean async, final Runnable next)
{
if (uri == null)
return null;
final String hashString = EncryptionManager.getInstance().createHash(context, uri.toString());
final File cacheDir = ImageUtils.fetchCacheDir(context);
final File cachedFile = new File(cacheDir, hashString);
if (cachedFile.exists() && cachedFile.length() > 0)
{
cachedFile.setLastModified(System.currentTimeMillis());
return Uri.fromFile(cachedFile);
}
Runnable r = new Runnable()
{
public void run()
{
File tempFile = null;
try
{
long freeSpace = ImageUtils.cacheSpace(context);
if (freeSpace < 4096 * 4096)
{
ImageUtils.cleanCache(context);
freeSpace = ImageUtils.cacheSpace(context);
if (freeSpace < 4096 * 4096)
ImageUtils.clearCache(context);
}
freeSpace = ImageUtils.cacheSpace(context);
if (freeSpace > 1024 * 1024)
{
URL u = new URL(uri.toString());
URLConnection conn = u.openConnection();
InputStream in = conn.getInputStream();
tempFile = File.createTempFile(hashString, "tmp", cacheDir);
FileOutputStream out = new FileOutputStream(tempFile);
byte[] buffer = new byte[8192];
int read = 0;
while ((read = in.read(buffer, 0, buffer.length)) != -1)
{
out.write(buffer, 0, read);
}
out.close();
tempFile.renameTo(cachedFile);
}
}
catch (SocketTimeoutException e)
{
LogManager.getInstance(context).logException(e);
}
catch (SocketException e)
{
LogManager.getInstance(context).logException(e);
}
catch (SSLException e)
{
LogManager.getInstance(context).logException(e);
}
catch (UnknownHostException e)
{
LogManager.getInstance(context).logException(e);
}
catch (EOFException e)
{
LogManager.getInstance(context).logException(e);
}
catch (FileNotFoundException e)
{
LogManager.getInstance(context).logException(e);
}
catch (IOException e)
{
LogManager.getInstance(context).logException(e);
}
finally
{
if (tempFile != null && tempFile.exists())
tempFile.delete();
if (next != null)
next.run();
}
}
};
if (async)
{
Thread t = new Thread(r);
t.start();
return null;
}
r.run();
return ImageUtils.fetchCachedUri(context, uri, true, next);
}
private static File fetchCacheDir(Context context)
{
long now = System.currentTimeMillis();
if (ImageUtils._cacheDir == null || now - ImageUtils._lastCacheAccess > 1000)
{
File cache = context.getCacheDir();
if (cache.exists() == false)
cache.mkdirs();
ImageUtils._cacheDir = cache;
}
ImageUtils._lastCacheAccess = now;
return ImageUtils._cacheDir;
}
@SuppressWarnings("deprecation")
public static synchronized long cacheSpace(Context context)
{
long now = System.currentTimeMillis();
if (now - ImageUtils._lastSpaceChecked < 60000)
return ImageUtils._lastSpaceFree;
ImageUtils._lastSpaceChecked = now;
File cache = context.getCacheDir();
if (cache.exists() == false)
cache.mkdirs();
StatFs stat = new StatFs(cache.getAbsolutePath());
ImageUtils._lastSpaceFree = ((long) stat.getBlockSize()) * ((long) stat.getAvailableBlocks());
return ImageUtils._lastSpaceFree;
}
public static synchronized void cleanCache(Context context)
{
File cache = context.getCacheDir();
long cacheSize = 1024;
cacheSize = cacheSize * 1024 * 1024;
if (cache.exists() == false)
cache.mkdirs();
synchronized(context.getApplicationContext())
{
List<File> files = new ArrayList<File>();
for (File f : cache.listFiles())
{
files.add(f);
}
try
{
Collections.sort(files, new Comparator<File>() {
public int compare(File first, File second) {
if (first.lastModified() < second.lastModified())
return 1;
else if (first.lastModified() > second.lastModified())
return -1;
return first.getName().compareTo(second.getName());
}
});
}
catch (IllegalArgumentException e)
{
LogManager.getInstance(context).logException(e);
}
List<File> toRemove = new ArrayList<File>();
long totalCached = 0;
for (File f : files)
{
if (totalCached < cacheSize)
totalCached += f.length();
else
toRemove.add(f);
}
for (File f : toRemove)
{
f.delete();
}
}
ImageUtils._lastSpaceChecked = 0;
}
public static synchronized void clearCache(Context context)
{
File cache = context.getCacheDir();
if (cache.exists() == false)
cache.mkdirs();
List<File> files = new ArrayList<File>();
for (File f : cache.listFiles())
{
files.add(f);
}
for (File f : files)
{
f.delete();
}
ImageUtils._lastSpaceChecked = 0;
}
}